<?php

namespace app\index\service\payments;

use app\common\job\TradeRefund;
use app\common\job\TransferBatchResultQuery;
use app\common\model\AccountBill;
use app\common\model\Member;
use app\common\model\Trade;
use app\common\model\Transfer;
use app\common\model\TransferBatch;
use app\common\service\AccountBillService;
use app\common\service\TemplateSmsService;
use GuzzleHttp\Exception\RequestException;
use ShidianUtil\Concurrent\Synchronizer;
use ShidianUtil\Utils\StringUtil;
use think\Collection;
use think\exception\ValidateException;
use think\facade\Config;
use think\facade\Log;
use think\facade\Queue;
use think\Request;
use WeChatPay\Builder;
use WeChatPay\BuilderChainable;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

/**
 * 微信支付
 * Class AccountPaymentService
 * @package app\index\service\payments
 */
class WxpayPaymentService implements PaymentInterface, PaymentNotifyInterface, TransferBatchInterface
{
    private Synchronizer $synchronizer;

    public function __construct(Synchronizer $synchronizer)
    {
        $this->synchronizer = $synchronizer;
    }

    // **************** 支付 ****************/

    /**
     * 支付预下单
     * @param Trade $trade
     * @return array
     */
    public function pay(Trade $trade, bool $arrears = false): array
    {
        $scene = $trade->payMethodScene;
        if ($scene === PAYMENT_WXPAY_JSAPI) {
            return $this->payjsapi($trade);
        } elseif ($scene === PAYMENT_WXPAY_H5) {
            return $this->payh5($trade);
        }

        throw new ValidateException('不支持该支付场景');
    }

    /**
     * jsapi预下单
     * @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
     * @param Trade $trade
     * @return array
     */
    private function payjsapi(Trade $trade)
    {

        // 创建微信支付预下单订单，组装参数给前端用
        $config = Config::get('pay.wechatpay', []);
        $merchantId = $config['merchantId'] ?? '';
        $appid = Config::get('wechat.' . MINIPROGRAM . '.AppID', '');
        $description = '熊猫律师咨询';
        $out_trade_no = $trade->orderNo;
        $total_fee = (int)bcmul($trade->payMoney, 100);
        $notify_url = $config['notify_url'] ?? '';
        $openid = Member::where('id', $trade->memberId)->value('openidWeiXin');

        $data = ['json' => [
            'appid' => $appid,
            'mchid' => $merchantId,
            'description' => $description,
            'out_trade_no' => $out_trade_no,
            // 'time_expire' => '2015-05-20T13:29:35+08:00'
            'notify_url' => $notify_url,
            'amount' => [
                'total' => $total_fee,
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $openid,
            ]
        ]];

        $instance = $this->getWechatPayInstanceV3();

        // 生成微信预付单
        try {

            $response = $instance->chain('v3/pay/transactions/jsapi')
                ->post($data);

            $body = (string)$response->getBody();

            $result = json_decode($body, true);
        } catch (\Exception $e) {

            Log::error('微信下单失败' . $e->getMessage());
            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();
                Log::error($body);
            }

            throw new ValidateException('下单失败');
        }

        // 判断返回值是否正确
        if (is_blank($result['prepay_id'] ?? null)) {
            Log::error('微信下单失败没有prepay_id');

            throw new ValidateException('下单失败');
        }

        // 组装参数
        $merchantPrivateKeyFilePath = 'file://' . $config['merchantPrivateKeyFilePath'] ?? '';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        $params = [
            'appId' => $appid,
            'timeStamp' => (string)Formatter::timestamp(),
            'nonceStr' => Formatter::nonce(),
            'package' => 'prepay_id=' . $result['prepay_id'],
        ];
        $params += ['paySign' => Rsa::sign(Formatter::joinedByLineFeed(...array_values($params)), $merchantPrivateKeyInstance), 'signType' => 'RSA'];

        return $params;
    }

    /**
     * h5预下单
     * @link https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml
     * @param Trade $trade
     * @return array
     */
    private function payh5(Trade $trade)
    {
        // 创建微信支付预下单订单，组装参数给前端用
        $config = Config::get('pay.wechatpay', []);
        $merchantId = $config['merchantId'] ?? '';
        $appid = Config::get('wechat.' . MINIPROGRAM . '.AppID', '');
        $description = '熊猫律师咨询';
        $out_trade_no = $trade->orderNo;
        $total_fee = (int)bcmul($trade->payMoney, 100);
        $notify_url = $config['notify_url'] ?? '';
        // $openid = Member::where('id', $trade->memberId)->value('openidWeiXin');

        $data = ['json' => [
            'appid' => $appid,
            'mchid' => $merchantId,
            'description' => $description,
            'out_trade_no' => $out_trade_no,
            // 'time_expire' => '2015-05-20T13:29:35+08:00'
            'notify_url' => $notify_url,
            'amount' => [
                'total' => $total_fee,
                'currency' => 'CNY'
            ],
            'scene_info' => [
                'payer_client_ip' => \think\facade\Request::ip(),
                'h5_info' => [
                    'type' => 'Wap',
                    'app_url' => 'https://m.xiongmaolvshi.cn',
                ]
            ],
        ]];

        $instance = $this->getWechatPayInstanceV3();

        // 生成微信预付单
        try {

            $response = $instance->chain('v3/pay/transactions/h5')
                ->post($data);

            $body = (string)$response->getBody();

            $result = json_decode($body, true);
        } catch (\Exception $e) {

            Log::error('微信下单失败' . $e->getMessage());
            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();
                Log::error($body);
            }

            throw new ValidateException('下单失败');
        }

        // 判断返回值是否正确
        if (is_blank($result['h5_url'] ?? null)) {
            Log::error('微信下单失败没有 h5_url');

            throw new ValidateException('下单失败');
        }

        return [
            'h5_url' => $result['h5_url'],
        ];
    }

    /**
     * 支付回调
     * @param Request $request
     * @return array|mixed|string[]
     */
    public function payCallback(Request $request)
    {
        $inWechatpaySignature = $request->header('Wechatpay-Signature');// 请根据实际情况获取
        $inWechatpayTimestamp = $request->header('Wechatpay-Timestamp');// 请根据实际情况获取
        $inWechatpayNonce = $request->header('Wechatpay-Nonce');// 请根据实际情况获取
        $inBody = file_get_contents('php://input');

        Log::info('微信支付回调参数：' . $inBody);

        // 检查通知时间偏移量，允许5分钟之内的偏移
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        if (!$timeOffsetStatus) {
            // 错误
            Log::error('微信支付回调，时间不正确');
            return $this->responsev3('FAIL', '时间不正确');
        }

        // 验签
        $verifiedStatus = $this->verifiedSign($inWechatpaySignature, $inWechatpayTimestamp, $inWechatpayNonce, $inBody);
        if (!$verifiedStatus) {
            Log::error('微信支付回调，验签失败');
            return $this->responsev3('FAIL', '验签失败');
        }

        // 解密参数
        $params = $this->decodeParams($inBody);


        $openid = $params['payer']['openid'] ?? '';
        $total_fee = $params['amount']['total'] ?? 0;
        $total_fee = bcdiv($total_fee, 100, 2);
        $orderNo = $params['out_trade_no'] ?? null;
        $transaction_id = $params['transaction_id'] ?? '';
        $trade_state = $params['trade_state'] ?? null;

        // 支付结果是不成功
        if ($trade_state !== 'SUCCESS') {
            Log::warning('微信支付，回调的支付结果不是成功');
            return $this->responsev3('FAIL', '结果不是成功');
        }

        if (is_blank($orderNo)) {
            Log::warning('微信支付，回调没有本地单号');
            return $this->responsev3('FAIL', '没有本地单号');
        }

        return $this->paySuccess($orderNo, $transaction_id, $openid, $total_fee);
    }


    /**
     * @param $code
     * @param $message
     * @return array|string[]
     */
    private function responsev3($code = 'FAIL', $message = '')
    {
        return [
            'code' => $code,
            'message' => $message,
        ];
    }

    /**
     * 支付取消预下单
     * @param Trade $trade
     * @return void
     */
    public function cancelPay(Trade $trade): void
    {
        // 已付款就不能取消
        if ($trade->payStatus != Trade::UNPAID) {
            throw new ValidateException('该订单已支付，不能取消');
        }

        $merchantId = Config::get('pay.wechatpay.merchantId');
        $out_trade_no = $trade->orderNo;

        $data = ['json' => [
            'mchid' => $merchantId,
        ]];

        $instance = $this->getWechatPayInstanceV3();

        // 发起请求
        try {

            $response = $instance->chain('v3/pay/transactions/out-trade-no/' . $out_trade_no . '/close')
                ->post($data);

            // 没有错误就正确的

        } catch (\Exception $e) {

            Log::error('微信取消订单失败' . $e->getMessage());

            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();
                Log::error($body);
            }

            throw new ValidateException('取消失败');
        }

    }

    /**
     * 主动查询支付结果
     * @param Trade $trade
     * @return void
     */
    public function query(Trade $trade): void
    {

        // 不是待付款（付款中）就不查询
        if ($trade->payStatus != Trade::UNPAID) {
            return;
        }

        $merchantId = Config::get('pay.wechatpay.merchantId');
        $out_trade_no = $trade->orderNo;

        $data = ['json' => [
            'mchid' => $merchantId,
        ]];

        $instance = $this->getWechatPayInstanceV3();

        // 发起请求
        try {

            $response = $instance->chain('v3/pay/transactions/out-trade-no/' . $out_trade_no)
                ->get($data);

            $body = (string)$response->getBody();

            $result = json_decode($body, true);

        } catch (\Exception $e) {

            Log::error('微信查询订单失败' . $e->getMessage());

            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();
                Log::error($body);
            }

            throw new ValidateException('查询失败');
        }

        $trade_state = $result['trade_state'] ?? null;
        $orderNo = $result['out_trade_no'] ?? '';
        $transaction_id = $result['transaction_id'] ?? '';
        $openid = $params['payer']['openid'] ?? '';
        $total_fee = $params['amount']['total'] ?? 0;
        $total_fee = bcdiv($total_fee, 100, 2);

        // 支付结果是不成功
        if ($trade_state !== 'SUCCESS') {
            return;
        }

        $this->paySuccess($orderNo, $transaction_id, $openid, $total_fee);
    }

    // **************** 退款 ****************/

    // 发起退款
    public function refund(Trade $trade): bool
    {
        // 只有未开始和已失败可以发起退款
        if (!in_array($trade->refundResult, [Trade::UNSTART, Trade::FAIL])) {
            return false;
        }

        // 进入退款中状态
        $trade->refundResult = Trade::STARTED;
        if (is_blank($trade->refundNo)) {
            $trade->refundNo = 'REFUND' . $trade->orderNo;
        }
        $trade->save();

        $out_trade_no = $trade->orderNo;
        $out_refund_no = $trade->refundNo;
        $total_fee = (int)bcmul($trade->payMoney, 100);
        $notify_url = Config::get('pay.wechatpay.refund_notify_url', '');

        $data = ['json' => [
            'out_trade_no' => $out_trade_no,
            'out_refund_no' => $out_refund_no,
            'amount' => [
                'total' => $total_fee,
                'refund' => $total_fee,
                'currency' => 'CNY'
            ],
            'notify_url' => $notify_url,
        ]];

        $instance = $this->getWechatPayInstanceV3();

        // 发起退款
        try {

            $response = $instance->chain('v3/refund/domestic/refunds')
                ->post($data);

            return true;
        } catch (\Exception $e) {

            Log::error('微信退款请求失败' . $e->getMessage());

            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();
                Log::error($body);
                $result = json_decode($body, true);
                if (is_array($result)) {
                    $code = $result['code'] ?? null;
                    if ($code == 'NOT_ENOUGH') {
                        $this->getTemplateSmsService()->wechatMoneyNotEnough();
                    }
                }
            }

            // 三方转账结果保存为失败，方便重试
            $trade->refundResult = Trade::UNSTART;
            $trade->save();

            return false;
        }
    }

    // 退款回调
    public function refundCallback(Request $request)
    {
        $inWechatpaySignature = $request->header('Wechatpay-Signature');// 请根据实际情况获取
        $inWechatpayTimestamp = $request->header('Wechatpay-Timestamp');// 请根据实际情况获取
        $inWechatpayNonce = $request->header('Wechatpay-Nonce');// 请根据实际情况获取
        $inBody = file_get_contents('php://input');

        // 验签
        $verifiedStatus = $this->verifiedSign($inWechatpaySignature, $inWechatpayTimestamp, $inWechatpayNonce, $inBody);
        if (!$verifiedStatus) {
            Log::error('微信退款回调，验签失败');
            return $this->responsev3('FAIL', '验签失败');
        }

        // 解密参数
        $params = $this->decodeParams($inBody);


        $refund_fee = $params['amount']['refund'] ?? 0;
        $refund_fee = bcdiv($refund_fee, 100, 2);
        $orderNo = $params['out_trade_no'] ?? null;
        $refund_id = $params['refund_id'] ?? '';
        $refund_status = $params['refund_status'] ?? null;

        if (is_blank($orderNo)) {
            Log::warning('微信支付，退款回调没有本地单号');
            return $this->responsev3('FAIL', '缺少本地单号');
        }

        $success = false;
        $trade = null;

        $signature = Trade::class . $orderNo;
        $this->synchronizer->synchronized($signature, function () use (&$success, &$trade, $orderNo, $refund_id, $refund_status) {

            $trade = Trade::where('orderNo', $orderNo)->find();

            if (!$trade) {
                return;
            }

            // 不在退款中
            if ($trade->refundStatus != Trade::REFUNDING || $trade->refundResult != Trade::STARTED) {
                return;
            }

            // 保存订单状态
            $trade->refundNid = $refund_id;
            $trade->refundTime = time();
            if (StringUtil::equals($refund_status, 'SUCCESS')) {
                $trade->refundStatus = Trade::REFUNDED;
                $trade->refundResult = Trade::SUCCESS;
            } else {
                $trade->refundResult = Trade::FAIL;
            }
            $trade->save();

            $success = true;
        });

        if (!$success) {
            Log::warning('微信支付，回调订单不存在或状态不正确');
            return $this->responsev3('FAIL', '回调订单不存在或状态不正确');
        }

        if (StringUtil::equals($refund_status, 'SUCCESS')) {
            // 回调结果是成功

            $tradeId = $trade->id;
            $tradeType = $trade->tradeType;
            $describe = $trade->describe;

            // 查询该订单的支付账单
            $sourceId = AccountBill::where('tradeId', $tradeId)
                ->where('type', 'pay')
                ->where('billType', $tradeType)
                ->value('id');

            // 插入账单
            $this->getAccountBillService()->insertRefundThird($trade->memberId, '', PAYMENT_WXPAY, $refund_fee, $tradeType, $describe, $tradeId, $sourceId);

        } else {
            // 回调结果是失败

            Log::error('消费队列：微信退款回调结果失败，重新添加退款任务，订单ID=' . $trade->id);

            // 重新发起退款，添加退款任务
            $job_data = ['id' => $trade->id];
            Queue::push(TradeRefund::class, $job_data);
        }

        return $this->responsev3('SUCCESS');
    }

    // **************** 转账 ****************/

    /**
     * @param TransferBatch $transferBatch
     * @param Collection $collection
     * @return bool
     */
    public function transferBatch(TransferBatch $transferBatch, Collection $collection): bool
    {
        if ($collection->isEmpty()) {
            return true;
        }

        $config = Config::get('pay.wechatpay', []);

        $mch_appid = $config['mch_appid'] ?? '';

        // 发起批量转账请求
        $transfer_detail_list = [];
        $collection->each(function (Transfer $transfer) use (&$transfer_detail_list) {
            $transfer_detail_list[] = [
                'out_detail_no' => $transfer->orderNo,
                'transfer_amount' => (int)bcmul($transfer->totalMoney, 100),
                'transfer_remark' => $transfer->describe,
                'openid' => $transfer->accountNumber,
                // 'user_name' =>'明细转账金额 >= 2,000元，收款用户姓名必填',
            ];
        });
        $data = ['json' => [
            'appid' => $mch_appid,
            'out_batch_no' => $transferBatch->orderNo,
            'batch_name' => $transferBatch->describe,
            'batch_remark' => $transferBatch->describe,
            'total_amount' => (int)bcmul($transferBatch->totalMoney, 100),
            'total_num' => $transferBatch->totalNum,
            'transfer_detail_list' => $transfer_detail_list,
        ]];

        $instance = $this->getWechatPayInstanceV3();

        // 生成微信预付单
        $response = null;
        try {

            $response = $instance->chain('v3/transfer/batches')
                ->post($data);

            $result = json_decode((string)$response->getBody(), true);
        } catch (\Exception $e) {

            Log::error('微信批量转账请求异常' . $e->getMessage());

            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();

                Log::error($body);

                $result = json_decode($body, true);
                if (is_array($result)) {
                    $code = $result['code'] ?? null;
                    if ($code == 'NOT_ENOUGH') {
                        $this->getTemplateSmsService()->wechatMoneyNotEnough();
                    }
                }
            }

            return false;
        }

        if (not_blank($result['out_batch_no'] ?? null) && not_blank($result['batch_id'] ?? null)) {
            // 成功

            // 保存微信单号
            $transferBatch->tradeNid = $result['batch_id'];
            $transferBatch->save();

            // 插入查询任务，在一定时间后查询转账结果
            $collection->each(function (Transfer $transfer) {
                // 保存为转账中状态
                $transfer->transferStatus = 1;
                $transfer->save();

                $job_data = ['id' => $transfer->id];
                Queue::later(180, TransferBatchResultQuery::class, $job_data);
            });

            // 发短信提醒审核
            $this->getTemplateSmsService()->cashRemind();
            return true;
        } else {
            // 失败

            Log::error('微信批量转账请求失败了' . json_encode($result));

            // 发短信提醒余额不足了
            $this->getTemplateSmsService()->wechatMoneyNotEnough();

            return false;
        }
    }

    /**
     * 转账结果查询
     * @param Transfer $transfer
     * @return bool|null
     */
    public function transferResultQuery(Transfer $transfer): ?bool
    {

        $out_batch_no = $transfer->transferBatchOrderNo;
        $out_detail_no = $transfer->orderNo;

        $data = [
            'out_batch_no' => $out_batch_no,
            'out_detail_no' => $out_detail_no,
            // 'json' => [ ]
        ];

        $instance = $this->getWechatPayInstanceV3();

        // 查询微信批量转账结果
        try {

            $response = $instance
                ->v3->transfer->batches->outBatchNo->_out_batch_no_->details->outDetailNo->_out_detail_no_
                ->get($data);

            $result = json_decode((string)$response->getBody(), true);
        } catch (\Exception $e) {

            Log::error('微信批量转账查询异常' . $e->getMessage());

            if ($e instanceof RequestException) {
                $body = (string)$e->getResponse()->getBody();
                Log::error($body);
            }

            return null;
        }

        Log::notice('微信转账结果查询 $result =' . json_encode($result));

        // 判断结果
        // INIT: 初始态。 系统转账校验中
        // WAIT_PAY: 待确认。待商户确认, 符合免密条件时, 系统会自动扭转为转账中
        // PROCESSING:转账中。正在处理中，转账结果尚未明确
        // SUCCESS:转账成功
        // FAIL:转账失败。需要确认失败原因后，再决定是否重新发起对该笔明细单的转账（并非整个转账批次单）
        $status = $result['detail_status'] ?? null;
        if ($status != 'SUCCESS' && $status != 'FAIL') {
            return null;
        }

        if ($status === 'SUCCESS') {
            // 成功

            $transfer->transferStatus = '2';
            $transfer->transferTime = time();
            $transfer->tradeNid = $result['detail_id'] ?? '';
            $transfer->save();

            // 转账成功或者失败后，回调业务逻辑类
            event('TransferComplete', $transfer);

            // 插入转账账单
            $direction = $transfer->direction == 1;
            $memberType = $transfer->memberType;
            $memberId = $transfer->memberId;
            $accountNumber = $transfer->accountNumber;
            $money = $transfer->totalMoney;
            $transferType = $transfer->transferType;
            $describe = $transfer->describe;
            $transferId = $transfer->id;
            $cashId = $transfer->cashId;

            $this->getAccountBillService()->insertTransferThird($direction, $memberType, $memberId, $accountNumber, PAYMENT_WXPAY, $money, $transferType, $describe, $transferId, $cashId);

            return true;
        } elseif ($status === 'FAILED' || $status === 'FAIL') {
            // 失败

            $transfer->transferStatus = '3';
            $transfer->transferTime = time();
            $transfer->tradeNid = $result['detail_id'] ?? '';
            $transfer->save();

            // 转账成功或者失败后，回调业务逻辑类
            event('TransferComplete', $transfer);

            return false;
        }

        return null;
    }

    /**
     * 微信转账是否可用
     * @param $memberId
     * @return bool
     */
    public function isEnableTransfer($memberId): bool
    {
        // 授权了公众号openid才可以发起提现
        $openidWechat = Member::where('id', $memberId)->where('followedWechat', '1')->value('openidWechat');
        return not_blank($openidWechat);
    }

    /**
     * 获取微信支付实例V3
     * @return BuilderChainable
     */
    private function getWechatPayInstanceV3(): BuilderChainable
    {

        $config = Config::get('pay.wechatpay', []);

        $merchantId = $config['merchantId'] ?? '';

        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = 'file://' . $config['merchantPrivateKeyFilePath'] ?? '';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        // 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = $config['merchantCertificateSerial'];

        // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        $platformCertificateFilePath = 'file://' . $config['platformCertificateFilePath'];
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

        // 构造一个 APIv3 客户端实例
        return Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs' => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);
    }

    private function getAccountBillService(): AccountBillService
    {
        return app(AccountBillService::class);
    }

    /**
     * 验签
     * @param $inWechatpaySignature
     * @param $inWechatpayTimestamp
     * @param $inWechatpayNonce
     * @param $inBody
     * @return bool
     */
    private function verifiedSign($inWechatpaySignature, $inWechatpayTimestamp, $inWechatpayNonce, $inBody): bool
    {
        $platformCertificateFilePath = 'file://' . Config::get('pay.wechatpay.platformCertificateFilePath');
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        return Rsa::verify(
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $platformPublicKeyInstance
        );
    }

    /**
     * 获取输入参数
     * @param $inBody
     * @return void
     */
    private function decodeParams($inBody): array
    {
        // 在商户平台上设置的APIv3密钥
        $apiv3Key = Config::get('pay.wechatpay.apiv3Key');

        // 转换通知的JSON文本消息为PHP Array数组
        $inBodyArray = (array)json_decode($inBody, true);

        // 使用PHP7的数据解构语法，从Array中解构并赋值变量
        ['resource' => [
            'ciphertext' => $ciphertext,
            'nonce' => $nonce,
            'associated_data' => $aad
        ]] = $inBodyArray;

        // 加密文本消息解密
        $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);

        // 把解密后的文本转换为PHP Array数组
        return (array)json_decode($inBodyResource, true);
    }

    private function getTemplateSmsService(): TemplateSmsService
    {
        return app(TemplateSmsService::class);
    }

    /**
     * 支付成功结果处理
     * @param $orderNo
     * @param $transaction_id
     * @param $openid
     * @param float $total_fee
     * @return array|string[]
     */
    private function paySuccess($orderNo, $transaction_id, $openid, $total_fee): array
    {
        $success = false;
        $trade = null;

        $signature = Trade::class . $orderNo;
        $this->synchronizer->synchronized($signature, function () use (&$success, &$trade, $orderNo, $transaction_id) {

            $trade = Trade::where('orderNo', $orderNo)->find();

            if (!$trade) {
                return;
            }

            if ($trade->payStatus != Trade::UNPAID) {
                return;
            }

            $trade->payMemberType = 'member';
            $trade->payMemberId = $trade->memberId;

            // 保存已支付的状态
            $trade->tradeNid = $transaction_id;
            $trade->payStatus = Trade::PAID;
            $trade->payTime = time();
            $trade->save();

            $success = true;
        });

        if (!$success) {
            if (!$trade) {
                Log::warning('微信支付结果，本地订单不存在');
                return $this->responsev3('FAIL', '本地订单不存在');
            }

            Log::warning('微信支付结果，本地订单状态不正确 ID ' . $trade->id . ' payStatus' . $trade->payStatus);
            return $this->responsev3('FAIL', '本地订单状态不正确');
        }

        // 插入账单
        $tradeId = $trade->id;
        $tradeType = $trade->tradeType;
        $describe = $trade->describe;
        $this->getAccountBillService()->insertPayThird($trade->memberId, $openid, PAYMENT_WXPAY, $total_fee, $tradeType, $describe, $tradeId);

        // 触发支付成功事件
        event('PaySuccess', $trade);

        return $this->responsev3('SUCCESS');
    }
}
